"""
Views which work with Snippets, allowing them to be added, modified,
rated and viewed according to various criteria.

"""
from django.core.urlresolvers import reverse
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render_to_response
from django.template import RequestContext
from django.views.generic import list_detail, create_update
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.conf import settings
from django.db.models import Sum
from cab import forms
from cab.models import Language, Rating, Snippet
from bookmarks import base_generic_dict


@login_required
def create_or_edit_snippet(request, snippet_id=None):
    """
    Allows a user to create or edit an Snippet.
   
    """
    preview = False
    if snippet_id:
        snippet = get_object_or_404(Snippet,
                                pk=snippet_id,
                                author__pk=request.user.id)      
        orig_id = None
        form_cls = forms.EditSnippetForm
        title = 'Editing "%s"' % snippet.title
        action = "edit"
    else:
        orig = None
        orig_id = request.GET.get('oid', None)        
        if orig_id:
            try:
                orig = Snippet.objects.get(pk=orig_id)
            except Snippet.DoesNotExist:
                orig = None
        snippet = Snippet(author=request.user, original=orig)
        form_cls = forms.AddSnippetForm
        title = "Add a snippet"
        action = "add"
    if request.method == 'POST':
        form = form_cls(request.POST, instance=snippet)
        if form.is_valid():
            snippet = form.save(commit=False)
            if "submit" in request.POST:
                snippet.save()
                return HttpResponseRedirect(snippet.get_absolute_url())
            else:
                snippet.render_markup()
                preview = True
    else:
        form = form_cls(instance=snippet)
    return render_to_response('cab/create_or_edit_snippet.html',
                              { 'form': form,
                                'title': title,
                                'action': action,
                                'preview': preview,
                                'snippet': snippet},
                              context_instance=RequestContext(request))



def download(request, snippet_id):
    """
    Allows a user to download a Snippet's code.
   
    Context::
        None, returns a downloadable file.
   
    Template::
        None, returns a downloadable file.
   
    """
    snippet = get_object_or_404(Snippet, pk=snippet_id)
    response = HttpResponse(snippet.code, mimetype='text/plain')
    response['Content-Disposition'] = 'attachment; filename=%s.%s' % (snippet.title.replace(" ", "_"),
                                                                      snippet.language.file_extension)
    response['Content-Type'] = snippet.language.mime_type
    return response


@login_required
def rate_snippet(request, snippet_id):
    """
    Allows a user to rate a Snippet.
   
    Context::
        None, returns a redirect back to the Snippet.
   
    Template::
        None, returns a redirect back to the Snippet.
   
    """
    snippet = get_object_or_404(Snippet, pk=snippet_id)
   
    if not Rating.objects.already_rated(request.user.id, snippet.id):
        score = request.GET.get('score')
        if score:
            rating = Rating.objects.create(snippet=snippet,
                                           user=request.user,
                                           score={ 'up': 1,
                                                   'down': -1 }[score])
    return HttpResponseRedirect(snippet.get_absolute_url())

# users snippets are listed on the profile page
#def snippets_by_author(request, username):
#    """
#    List of Snippets submitted by a particular User.
   
#    Context::
#    Same as the generic ``list_detail.object_list`` view, with
#    one extra variable:
   
#        object
#            The User
   
#    Template::
#        cab/user_detail.html
   
#    """
#    user = get_object_or_404(User, username__exact=username)
#    return list_detail.object_list(request,
#                                   queryset=Snippet.objects.get_by_author(user.username),
#                                   extra_context={ 'object': user },
#                                   template_name='cab/user_detail.html',
#                                   **base_generic_dict)

def snippets_by_language(request, slug):
    """
    List of Snippets written in a particular Language.
   
    Context::
    Same as the generic ``list_detail.object_list`` view, with
    one extra variable:
   
        object
            The Language
   
    Template::
        cab/language_detail.html
   
    """
    language = get_object_or_404(Language, slug__exact=slug)
    return list_detail.object_list(request,
                                   queryset=Snippet.objects.get_by_language(slug),
                                   extra_context={ 'object': language },
                                   template_name='cab/snippet_list_by_language.html',
                                   **base_generic_dict)
                                   
#def snippets_by_rating(request):
#    rated = Rating.objects.values('snippet_id').annotate(score=Sum('score'))\
#                                                            .order_by("-score")
#    rated_ids = [d['snippet_id'] for d in rated]
#    positive_rated = rated.filter(score__gt=0)
#    negative_rated = rated.filter(score__lt=0)
#    not_rated = Snippet.objects.values('id').exclude(id__in=rated_ids)
#                # [{"score":0, "snippet": s} for s in 
#                #   Snippet.objects.exclude(id__in=rated_ids)]
#    snippets = list(positive_rated) + list(not_rated) + list(negative_rated)
#   # snippets = Snippet.objects.in_bulk()
#   # rated_snippets = [dict(d, snippet=snippets[d["snippet_id"]]) for d in rated]

#    paginator = Paginator(snippets, settings.SNIPPETS_PER_PAGE)
    
#    # Make sure page request is an int. If not, deliver first page.
#    try:
#        page = int(request.GET.get('page', '1'))
#    except ValueError:
#        page = 1

#    # If page request (9999) is out of range, deliver last page of results.
#    try:
#        snippets = paginator.page(page)
#    except (EmptyPage, InvalidPage):
#        snippets = paginator.page(paginator.num_pages)
#    return render_to_response('cab/snippet_list.html',
#                              {"paginator":paginator, "page_obj":snippets,
#                               "object_list":snippets.object_list, "rating":True},
#                              context_instance=RequestContext(request))


def random(request):
    try:
        s = Snippet.objects.order_by("?")[0]
        return HttpResponseRedirect(s.get_absolute_url())
    except IndexError:
        raise Http404


def snippet_detail(request, object_id, extra_context):
    """
    Detail view of a Snippet.
   
    Context::
        object
            The Snippet object.
   
        num_ratings
            The number of Ratings this Snippet has received.
   
        rating_score
            The sum of this Snippet's received ratings.
   
    Template::
        cab/snippet_detail.html
   
    """
    snippet = get_object_or_404(Snippet, pk=object_id)
    values = { 'object': snippet,
               'num_ratings': snippet.rating_set.count(),
               'rating_score': Rating.objects.score_for_snippet(snippet.id) }
    values.update(extra_context)
    return render_to_response('cab/snippet_detail.html',
                              values,
                              context_instance=RequestContext(request))
                              
                              
                              
@login_required
def delete(request, snippet_id):
    obj = get_object_or_404(Snippet, id=snippet_id)
    if not obj.author == request.user:
        request.user.message_set.create(message="You are not allowed to delete this snippet.")
        return HttpResponseRedirect(obj.get_absolute_url())
    retval = create_update.delete_object(request, Snippet, object_id=obj.id, 
                         post_delete_redirect=reverse("snippets"),
                         template_name="cab/snippet_delete.html",)
    # delete_object already adds a message
    #if isinstance(retval, HttpResponseRedirect):
    #    request.user.message_set.create(message="Deleted your snippet successfully.")
    return retval
    
                              
